package de.mnit.basis.db.sql;

import java.lang.reflect.Array;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;

import de.mnit.basis.daten.struktur.liste.Liste;
import de.mnit.basis.daten.struktur.liste.S_Liste;
import de.mnit.basis.daten.struktur.tabelle.S_Tabelle;
import de.mnit.basis.daten.struktur.tabelle.Tabelle;
import de.mnit.basis.db.DB_ZENTRAL;
import de.mnit.basis.db.sql.S_SqlDB;
import de.mnit.basis.fehler.Fehler;
import de.mnit.basis.sys.Ausgabe;

/**
 * @author Michael Nitsche
 */
public abstract class A_SqlDB implements S_SqlDB {

	private Statement  sql_statement;
	private Connection sql_connection;
	private boolean    verbunden=false;


	public A_SqlDB(String host, String db, String user, String pass, boolean erstellen) {
		if(erstellen) erstelle(host,db,user,pass);
		verbunden=verbinde(host,db,user,pass);
	}


	public S_Tabelle<String> gib(String s) {
		if(DB_ZENTRAL.DEBUG) Ausgabe.debug(s);
		S_Tabelle<String> erg=null;
		try {
			ResultSet rset=sql_statement.executeQuery(s);
			int breite=rset.getMetaData().getColumnCount();
			erg=Tabelle.neu(String.class,breite);

			while(rset.next()) {
				String[] zeile = (String[])Array.newInstance(String.class,breite);

				for(int i=1; i<=breite; i++) {
					Object o = rset.getObject(i);
					String t = iObjektZuString(o);
					zeile[i-1]=t;
				}
				erg.plus(zeile);
			}
		}
		catch(SQLException e) {
			Fehler.sonstige.da_Untypisch(s,e);
		}
		return erg;
	}

	public S_Liste<String> gibZeile(String s) {
		if(DB_ZENTRAL.DEBUG)
			Ausgabe.debug(s);
		S_Liste<String> erg=Liste.neu();
		try {
			ResultSet rset=sql_statement.executeQuery(s);
			if(rset.next()) {
				for(int i=1; i<=rset.getMetaData().getColumnCount(); i++) {
					Object o = rset.getObject(i);
					erg.plus(iObjektZuString(o));
				}
			}
		}
		catch(SQLException e) {
			Fehler.sonstige.da_Untypisch(s,e);
		}
		return erg;
	}

	public S_Liste<String> gibSpalte(String s) {
		if(DB_ZENTRAL.DEBUG)
			Ausgabe.debug(s);
		S_Liste<String> erg=Liste.neu();
		try {
			ResultSet rset=sql_statement.executeQuery(s);
			int breite=rset.getMetaData().getColumnCount();
			if(breite==0)
				return erg;
			while(rset.next()) {
				Object o = rset.getObject(1);
				erg.plus(iObjektZuString(o));
			}
		}
		catch(SQLException e) {
			Fehler.sonstige.da_Untypisch(s,e);
		}
		return erg;
	}

	public S_Liste<Integer> gibSpalteInt(String s) {
		if(DB_ZENTRAL.DEBUG)
			Ausgabe.debug(s);
		S_Liste<Integer> erg=Liste.neu();
		try {
			ResultSet rset=sql_statement.executeQuery(s);
			int breite=rset.getMetaData().getColumnCount();
			if(breite==0)
				return erg;
			while(rset.next()) {
				Object o = rset.getObject(1);
				erg.plus(iObjektZuInteger(o));
			}
		}
		catch(SQLException e) {
			Fehler.sonstige.da_Untypisch(s,e);
		}
		return erg;
	}

	public String gibString(String s) {
		try {
//			return iGibAtomar(s).getString(1);
			ResultSet rs=iGibAtomarWrapper(s);
			return rs==null || rs.getObject(1)==null
				? null
				: rs.getString(1); //Muss extra mit getObject geprüft werden, sonst > 0
		}
		catch(SQLException e) {
			throw Fehler.weitergeben(e);
		}
	}

	public boolean gibBool(String s) {
		try { return iGibAtomar(s).getBoolean(1); }
		catch(SQLException e) { throw Fehler.weitergeben(e); }
	}

	public double gibDouble(String s) {
		try { return iGibAtomar(s).getDouble(1); }
		catch(SQLException e) { throw Fehler.weitergeben(e); }
	}

	public float gibFloat(String s) {
		try { return iGibAtomar(s).getFloat(1); }
		catch(SQLException e) { throw Fehler.weitergeben(e); }
	}

	public int gibInt(String s) {
		try { return iGibAtomar(s).getInt(1); }
		catch(SQLException e) { throw Fehler.weitergeben(e); }
	}

	public long gibLong(String s) {
		try { return iGibAtomar(s).getLong(1); }
		catch(SQLException e) { throw Fehler.weitergeben(e); }
	}

	public Integer gibWInteger(String s) {
		try {
			ResultSet rs=iGibAtomarWrapper(s); //TODO auf iObjZuInt umbauen
			return rs==null || rs.getObject(1)==null ? null : rs.getInt(1); //Muss extra mit getObject geprüft werden, sonst > 0
		}
		catch(SQLException e) { throw Fehler.weitergeben(e, s); }
	}

	public boolean istLeer(String s) {
		return this.gib(s).gLaenge()==0;
	}

	public void befehl(String s) {
		if(DB_ZENTRAL.DEBUG)
			Ausgabe.debug(s);
		try {
			sql_statement.execute(s);
		}
		catch(SQLException e) {
			Fehler.sonstige.da_Untypisch(s,e);
		}
	}

	public void schliessen() {
		if(verbunden)
			try {
				sql_statement.close();
			    sql_connection.close();
			    verbunden=false;
			}
			catch(SQLException e) {
				Fehler.sonstige.da_Untypisch(e);
			}
	}

	public void autoCommit(boolean b)   {
		try { sql_connection.setAutoCommit(b); }
		catch(SQLException e) { Fehler.sonstige.da_Untypisch(e); }
	}

	public void commit()   {
		try { sql_connection.commit(); }
		catch(SQLException e) { Fehler.sonstige.da_Untypisch(e); }
	}

	public void rollback()   {
		try { sql_connection.rollback(); }
		catch(SQLException e) { Fehler.sonstige.da_Untypisch(e); }
	}

	public Connection connection() {
		return this.sql_connection;
	}


	protected abstract String jdbc(String host, String db);
	protected abstract String treiber();
	protected abstract void erstelle(String host, String db, String user, String pass);

	protected void finalize() throws Throwable {
		schliessen();
		verbunden=false;
		super.finalize();
	}

	// INTERN

	private boolean verbinde(String host, String db, String user, String pass) { //TODO Sollte keinen bool zurückgeben, sondern einen Fehler werfen!!!
		try {
			Class.forName( treiber() ); // Treiber laden

			// Angemeldete Treiber anzeigen
//			for( Enumeration e = DriverManager.getDrivers(); e.hasMoreElements(); )
//				  System.out.println( e.nextElement().getClass().getName() );

			sql_connection=(Connection)DriverManager.getConnection(jdbc(host,db), user, pass);
			sql_statement=sql_connection.createStatement();
			return true;
		}
		catch(Exception e)   { Fehler.sonstige.da_Untypisch(e); }
		return false;
	}

	private String iObjektZuString(Object o) {
		String t= o==null
			? null
			: o.toString();
		if(t!=null && o.getClass()==Timestamp.class)
			t = t.substring(0, 19);
		return t;
	}

	private Integer iObjektZuInteger(Object o) {
		return o==null ? null : Integer.parseInt(o.toString());
	}

	private ResultSet iGibAtomar(String s) {
		ResultSet erg=iGibAtomarWrapper(s);
		if(erg==null)
			Fehler.sonstige.da_Untypisch("Ein leeres Ergebnis kann nicht umgewandelt werden", s, erg);
		return erg;
	}

	private ResultSet iGibAtomarWrapper(String s) {
		if(DB_ZENTRAL.DEBUG)
			Ausgabe.debug(s);
		ResultSet erg=null;
		try {
			erg=sql_statement.executeQuery(s);
			Fehler.zahl.wenn_ZuKlein(1, erg.getMetaData().getColumnCount());
			if(!erg.next())
				erg=null;
		}
		catch(SQLException e) {
			Fehler.sonstige.da_Untypisch(s,e);
		}
		return erg;
	}

}
